/**
 * @Description:封装对接小米运动刷步：支持同步至微信、支付宝，小米运动账号支持手机/邮箱号
 * @Version: 1.0
 * @Author: 琴时
 */
const axios = require('axios')
import { dateFormat, getTimeDistance } from './time'
import jsonData, { summary } from './jsonData'
import { randomUserAgent, returnIp } from './device'
import { parseExcel, jsonToList, exportExcelFile } from './file'
import { openTimer } from './schedule'
import { excelPath, indexSection, stepsSection, concurrency } from '../config'
import { sendNodeMail } from './email' //引入加解密方法
const deviceId = 'd98c80014a4255ca' //设备ID
const responseData = {
  code: 200,
  data: null,
  message: 'success',
}
const requestPromise = async params => {
  return axios({
    url: params.url,
    method: params.method,
    headers: params.headers,
    data: new URLSearchParams(params.body),
    validateStatus: status => {
      return status >= 200 && status < 400
    },
    maxRedirects: 0,
  })
}

const checkMobile = value => {
  const pattern = /^\d{11}$/
  return pattern.test(value)
}
// 获取小米运动accessToken
const getAccessToken = async (username, password) => {
  try {
    const phoneUrl = `https://api-user.huami.com/registrations/+86${username}/tokens` //手机号登录接口
    const emailUrl = `https://api-user.huami.com/registrations/${username}/tokens` //邮箱号登录接口
    const url = checkMobile(username) ? phoneUrl : emailUrl
    const res = await requestPromise({
      url: url,
      body: {
        client_id: 'HuaMi',
        password: password,
        redirect_uri:
          'https://s3-us-west-2.amazonaws.com/hm-registration/successsignin.html',
        token: 'access',
      },
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8',
        'User-Agent': randomUserAgent('mobile'),
        'X-Forwarded-For': returnIp(),
      },
      method: 'POST',
    })
    const reg = /(?<=access=).*?(?=&)/
    return reg.exec(res.headers?.location)
  } catch (error) {
    console.log('获取accessToken异常:', error.message)
    return false
  }
}
// 获取小米运动登录信息
const getUserInfo = async accessToken => {
  try {
    const res = await requestPromise({
      url: 'https://account.huami.com/v2/client/login', //获取用户id接口
      body: {
        app_name: 'com.xiaomi.hm.health',
        app_version: '6.0.1',
        code: accessToken,
        country_code: 'unknown',
        device_id: deviceId,
        device_model: 'android_phone',
        grant_type: 'access_token',
        third_name: 'huami',
        allow_registration: 'false',
        dn: 'account.huami.com%2Capi-user.huami.com%2Capi-watch…m%2Capp-analytics.huami.com%2Capi-mifit.huami.com',
        lang: 'zh',
      },
      source: 'com.xiaomi.hm.health%3A6.0.1%3A50545',
      method: 'POST',
      headers: {
        'User-Agent':
          'Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2',
      },
    })
    const { login_token, user_id } = res.data?.token_info || {}
    return {
      loginToken: login_token,
      userid: user_id,
    }
  } catch (error) {
    console.log('获取登录信息异常:', error.message)
    return
  }
}
// 获取app_token
const getAppToken = async login_token => {
  try {
    const res = await requestPromise({
      url: `https://account-cn.huami.com/v1/client/app_tokens?app_name=com.xiaomi.hm.health&dn=api-user.huami.com%2Capi-mifit.huami.com%2Capp-analytics.huami.com&login_token=${login_token}&os_version=6.3.3`,
      method: 'GET',
    })
    const { app_token } = res.data?.token_info || {}
    return app_token
  } catch (error) {
    console.log('获取获取app_token异常:', error.message)
  }
}
// 修改步数
const changeSteps = async ({ app_token, userid }, step = 18000) => {
  const date = dateFormat('', 'YYYY-MM-DD')
  summary.stp.ttl = step //修改步数
  let params = jsonData[0]
  params.date = date
  params.did = deviceId
  params.summary = JSON.stringify(summary)
  const data_json = JSON.stringify([params])
  const t = Date.now()
  try {
    const { data: res } = await requestPromise({
      url: `https://api-mifit-cn.huami.com/v1/data/band_data.json?&t=${t}`,
      headers: {
        'User-Agent':
          'Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2',
        apptoken: app_token,
      },
      body: {
        data_json: data_json,
        userid: userid,
        device_type: '0',
        last_sync_data_time: (t / 1000 - 3600).toFixed(0),
        last_deviceid: deviceId,
      },
      method: 'POST',
    })
    console.log('修改步数changeSteps:', res)
    return res?.code == 1
  } catch (error) {
    console.log('步数修改异常:', error.message)
    return
  }
}
/*******
 * @description:程序入口
 * @author: 琴时
 * @param {*} username [运动账号]
 * @param {*} password [运动密码]
 * @param {*} step     [运动步数]
 * @return {*}
 */
const main = async (username, password, step) => {
  //1.获取access_token
  const accessToken = await getAccessToken(username, password)
  if (!accessToken) {
    responseData.code = 500
    responseData.message =
      accessToken === undefined ? '账号或密码不正确' : '系统繁忙，请稍后重试'
    responseData.data = null
    return responseData
  }
  //2.获取登录信息:userid、login_token
  const { loginToken = 0, userid } = (await getUserInfo(accessToken[0])) || {}
  if (loginToken === 0 || !userid) {
    responseData.code = 500
    responseData.message = '系统繁忙，请稍后重试'
    responseData.data = null
    return responseData
  }
  //3.获取app_token
  const app_token = await getAppToken(loginToken)
  if (!app_token) {
    responseData.code = 500
    responseData.message = '系统繁忙，请稍后重试'
    responseData.data = null
    return responseData
  }
  //4.修改运动步数
  const status = await changeSteps({ app_token, userid }, step)
  responseData.code = status ? 200 : 500
  responseData.message = status
    ? `【${step}】步成功提交`
    : '系统繁忙，请稍后重试'
  responseData.data = username
  return responseData
}

const asyncMain = ({ username, password, step }) => {
  return new Promise((resolve, reject) => {
    const res = main(username, password, step)
    resolve(res)
  })
}

/* 后台并联执行同步 */
const errorBatchTempObj = {} //失败数据缓存对象
const successBatchTempObj = {} //成功数据缓存对象
const errorBatchReqNumObj = {} //轮巡次数缓存对象
// 初始化缓存对象
export const initBatchTemp = field => {
  errorBatchTempObj[field] = []
  successBatchTempObj[field] = []
  errorBatchReqNumObj[field] = 0
}
// 清除缓存数据
export const resetBatchTemp = field => {
  delete errorBatchTempObj[field]
  delete successBatchTempObj[field]
  delete errorBatchReqNumObj[field]
}
export const batchSteps = async (
  field,
  dataList = [],
  initIndex = 0,
  stepsSection,
  callback
) => {
  try {
    if (!successBatchTempObj[field]) initBatchTemp(field)
    if (dataList.length === 0) {
      const successList = JSON.parse(JSON.stringify(successBatchTempObj[field]))
      const errorList = JSON.parse(JSON.stringify(errorBatchTempObj[field]))
      resetBatchTemp(field)
      callback && callback(successList, errorList)
      return
    }
    let tempList = dataList.splice(0, concurrency) //切割并发请求数据
    const startIndex = concurrency * initIndex //记录下次请求开始索引
    //构造并发请求列表
    const requestList = tempList.map(item => {
      const num =
        Math.floor(Math.random() * (stepsSection[1] - stepsSection[0])) +
        stepsSection[0] // 取steps[0]~steps[1]随机步数
      item.step = num
      const params = {
        username: item.username,
        password: item.password,
        step: num,
      }
      return asyncMain(params)
    })
    //获取并发请求响应数组
    const responseList = await Promise.all(requestList)
    // 解析并发请求响应
    responseList.forEach((res, index) => {
      tempList[index].message = res.message
      const params = tempList[index]
      index += startIndex + 1
      console.log(`${field}-第${index}个请求响应:`, res)
      if (res?.code === 502) {
        //参数配置错误导致服务终止
        errorBatchTempObj[field].push(params)
        throw { message: `系统终止，第${index}个账号执行失败` }
      }
      if (res.code !== 200) {
        errorBatchTempObj[field].push(params)
        return
      }
      successBatchTempObj[field].push(params)
    })
    initIndex++
    if (dataList.length === 0 && errorBatchTempObj[field].length > 0) {
      // 当前轮巡执行完成存在提交失败数据
      errorBatchReqNumObj[field]++
      const errorList = JSON.parse(JSON.stringify(errorBatchTempObj[field]))
      if (errorBatchReqNumObj[field] > 10) {
        const successList = JSON.parse(
          JSON.stringify(successBatchTempObj[field])
        )
        resetBatchTemp(field)
        callback && callback(successList, errorList)
        return
      }
      errorBatchTempObj[field] = []
      batchSteps(field, errorList, 0, stepsSection, callback)
      return
    }
    batchSteps(field, dataList, initIndex, stepsSection, callback)
  } catch (error) {
    console.log('并发异常:', error.message)
    const successList = JSON.parse(JSON.stringify(successBatchTempObj[field]))
    const errorList = JSON.parse(JSON.stringify(errorBatchTempObj[field]))
    resetBatchTemp(field)
    callback && callback(successList, errorList)
  }
}

// 后台同步执行刷步
const errorTempObj = {} //失败数据缓存对象
const successTempObj = {} //成功数据缓存对象
const errorReqNumObj = {} //轮巡次数缓存对象
export const initTemp = () => {
  errorTempObj[field] = []
  successTempObj[field] = []
  errorReqNumObj[field] = 0
}
export const resetTemp = field => {
  delete errorTempObj[field]
  delete successTempObj[field]
  delete errorReqNumObj[field]
}
const asyncSteps = async (field, dataList = [], index = 0, callback) => {
  try {
    if (!successTempObj[field]) initTemp(field)
    if (index > dataList.length) {
      // console.log(`${field}暂无可同步数据！`)
      const successList = JSON.parse(JSON.stringify(successTempObj[field]))
      const errorList = JSON.parse(JSON.stringify(errorTempObj[field]))
      callback && callback(successList, errorList)
      resetTemp(field)
      return
    }
    const num =
      Math.floor(Math.random() * (stepsSection[1] - stepsSection[0])) +
      stepsSection[0] // 取stepsSection[0]~stepsSection[1]随机步数
    const item = dataList[index] || {} //将要同步的数据
    const username = item?.username
    const password = item?.password
    const step = item?.step || num
    const res = await main(username, password, step)
    item.message = res.message
    item.step = num
    index++
    console.log(`${field}-第${index}个请求响应:`, res)
    if (res?.code === 502) {
      //参数配置错误导致服务终止
      errorTempObj[field].push(item)
      console.log(`${field}系统终止，第${index}个账号执行失败`)
      const successList = JSON.parse(JSON.stringify(successTempObj[field]))
      const errorList = JSON.parse(JSON.stringify(errorTempObj[field]))
      callback && callback(successList, errorList)
      resetTemp(field)
      return
    }
    if (res?.code !== 200) {
      errorTempObj[field].push(item)
      // console.log(`${field}第${index}个账号提交失败：`, item)
      if (index >= dataList.length) {
        // console.log(`${field}当前轮巡已执行完成.`)
        const errorList = JSON.parse(JSON.stringify(errorTempObj[field]))
        const successList = JSON.parse(JSON.stringify(successTempObj[field]))
        if (errorList.length > 0) {
          // console.log(`${field}存在失败的数据暂未同步成功：`,errorTempObj[field].length)
          errorReqNumObj[field]++
          if (errorReqNumObj > 10) {
            // 终止执行
            // console.log(`${field}存在账号多次同步失败，请自行查看日志文件分析`)
            callback && callback(successList, errorList)
            resetTemp(field)
            return
          }
          // console.log(`${field}第${errorReqNumObj[field]}次重试同步`)
          errorTempObj[field] = [] //清空失败缓存
          setTimeout(() => {
            asyncSteps(field, errorList, 0, callback)
          }, 5000)
          return
        }
        // console.log('暂无可同步的失败数据')
        callback && callback(successList, errorList)
        resetTemp(field)
      } else {
        // console.log('下一个即将请求...')
        setTimeout(() => {
          asyncSteps(field, dataList, index, callback)
        }, 1000)
      }
      return
    }
    successTempObj[field].push(item) //记录同步成功的数据
    const successList = JSON.parse(JSON.stringify(successTempObj[field]))
    const errorList = JSON.parse(JSON.stringify(errorTempObj[field]))
    if (index <= dataList.length) {
      const tempItem = JSON.parse(JSON.stringify(item))
      delete tempItem['password']
      // console.log(`${field}第${index}个账号提交成功：`, tempItem)
      if (index < dataList.length) {
        // 不是最后一个继续同步下一个
        // console.log(`${field}准备同步第${index + 1}个账号`)
        asyncSteps(field, dataList, index, callback)
      } else {
        //当前轮巡执行完检测是否存在没有同步成功的数据
        if (errorList > 0) {
          // console.log(`${field}当前轮巡已执行完成!`)
          // console.log(`${field}存在失败的数据暂未同步成功：`,errorTempObj[field].length)
          errorReqNumObj[field]++
          if (errorReqNumObj > 10) {
            // console.log(`${field}存在账号多次同步失败，请自行查看日志文件分析`)
            callback && callback(successList, errorList)
            resetTemp(field)
            return
          }
          // console.log(`${field}第${errorReqNumObj}次重试同步`)
          errorTempObj[field] = [] //清空失败缓存
          setTimeout(() => {
            asyncSteps(field, errorList, 0, callback)
          }, 5000)
          return
        }
        callback && callback(successList, errorList)
        resetTemp(field)
      }
    } else {
      // console.log(`${field}暂无第${index}账号，本次同步结束!`)
      callback && callback(successList, errorList)
      resetTemp(field)
    }
  } catch (error) {
    console.log(`${field}系统异常:`, error.message)
    let time = 3000
    console.log(`3秒后将继续提交同步，请稍等...`)
    setTimeout(() => {
      asyncSteps(field, dataList, index, callback)
    }, time)
  }
}

// 自动执行本地Excel【后台并发执行刷步】
export const autoLocalExcelBatchStep = field => {
  try {
    const result = parseExcel(excelPath, indexSection)
    //'0 5 5 * * *'==>每天:05:05开启自动运行(执行完后将邮箱提醒)
    openTimer(field, '0 5 5 * * *', () => {
      const startTime = Date.now() //记录开始时间
      batchSteps(
        field,
        result,
        0,
        stepsSection,
        async (successList, errorList) => {
          const endTime = Date.now() //记录结束时间
          const sumTime = getTimeDistance(startTime, endTime)
          let html = `
         【kimShift平台】${field}自动刷步日志：
         <p style="margin-left:2rem">同步总耗时：${sumTime?.timeStr}</p>
         <p style="margin-left:2rem">提交总数量：${result.length}</p>
         <p style="margin-left:2rem">成功总数量：${successList.length}</p>
         <p style="margin-left:2rem">失败总数量：${errorList.length}</p>
         `
          let config = { type: 'html', content: html, title: '自动刷步日志' }
          if (errorList.length > 0) {
            const excelConfig = [
              { key: 'username', title: '运动账号' },
              { key: 'password', title: '运动密码' },
              { key: 'step', title: '运动步数' },
              { key: 'message', title: '失败信息' },
            ]
            const res1 = await jsonToList(errorList, excelConfig) //将json转Excel数据源
            const res2 = await exportExcelFile(res1, 'errorBatchLog') //将Excel数据源导出
            config.attachments = [
              { filename: 'errorBatchLog.xlsx', path: res2 },
            ]
          }
          const email = '760402117@qq.com'
          sendNodeMail(email, config, (status, msg) => {
            if (!status) {
              return console.log('发送失败:', msg.message)
            }
          })
        }
      )
    })
  } catch (error) {
    console.log('自动执行本地异常:', error.message)
  }
}
// 自动执行本地Excel
export const autoLocalExcelStep = field => {
  try {
    const result = parseExcel(excelPath, indexSection)
    //'0 3 0 * * *'==>每天:00:03开启自动运行(执行完后将邮箱提醒)
    openTimer(field, '0 3 0 * * *', () => {
      const startTime = Date.now() //记录开始时间
      initTemp(field)
      asyncSteps(field, result, 0, async (successList, errorList) => {
        resetTemp(field)
        const endTime = Date.now() //记录结束时间
        const sumTime = getTimeDistance(startTime, endTime)
        let html = `
       【kimShift平台】${field}自动刷步日志：
       <p style="margin-left:2rem">同步总耗时：${sumTime?.timeStr}</p>
       <p style="margin-left:2rem">提交总数量：${result.length}</p>
       <p style="margin-left:2rem">成功总数量：${successList.length}</p>
       <p style="margin-left:2rem">失败总数量：${errorList.length}</p>
       `
        let config = { type: 'html', content: html, title: '自动刷步日志' }
        if (errorList.length > 0) {
          const excelConfig = [
            { key: 'username', title: '运动账号' },
            { key: 'password', title: '运动密码' },
            { key: 'step', title: '运动步数' },
            { key: 'message', title: '失败信息' },
          ]
          const res1 = await jsonToList(errorList, excelConfig) //将json转Excel数据源
          const res2 = await exportExcelFile(res1, 'errorLog') //将Excel数据源导出
          config.attachments = [{ filename: 'errorLog.xlsx', path: res2 }]
        }
        const email = '760402117@qq.com'
        sendNodeMail(email, config, (status, msg) => {
          if (!status) {
            return console.log('发送失败:', msg.message)
          }
        })
      })
    })
  } catch (error) {
    console.log('自动执行本地异常:', error.message)
  }
}

/*******
 * @description: 自动执行指定数据
 * @author: 琴时
 * @param {*} field      [字段标识]
 * @param {*} dataSource [数据源]
 */
export const autoStep = (field, dataSource) => {
  try {
    openTimer(field, '* * * * * *', () => {
      // console.log('测试result:', result.length)
      console.log('测试dataSource:', dataSource)
    })
  } catch (error) {}
}

export default main
